#ifndef DEFORMABLEOBJ_H
#define DEFORMABLEOBJ_H

#include <math.h>

#include "model_obj.h"
#include "vmatrix.h"
#include "lamatrix.h"

class DeformableMesh;

class VertexCluster
{
public:
	static const float EPSLON;

	VertexCluster( DeformableMesh& mesh, int clusterId );

	void addVertex( ModelOBJ::Vertex* vertex, ModelOBJ::Vertex* speed, ModelOBJ::Vertex* intermediatePosition, ModelOBJ::Vertex* intermediateSpeed, float weight );
	void addVertex( int v1Id, int v2Id, ModelOBJ::Vertex* vertex, ModelOBJ::Vertex* speed, ModelOBJ::Vertex* intermediatePosition, ModelOBJ::Vertex* intermediateSpeed, float weight );

	virtual void initialize();
	void calculateCenterOfMass( float* cm, std::vector<float>& pos, std::vector<float>& weight );
	void calculateCenterOfMass( float* cm, std::vector<ModelOBJ::Vertex*>& pos, std::vector<float>& weight );

	float update( float dt );
	//void consolidate();

	virtual void load();
	virtual void preLoad();

	virtual void updateInternal( float dt ) = 0;
	virtual void updateQVector( LAMatrix& Sp ) = 0;

	//PlasticityMode getPlasticityMode() const { return plasticityMode; }
	//void setPlasticityMode( PlasticityMode mode ) { plasticityMode = mode; }

	//void updateCenterOfMass( ModelOBJ::Vertex worldPos );
	//float applyCollisionConstraints( float dt );
	//void calculatingIntermediateSpeedAndPosition( float dt );

	void applyPlasticDeformation( float dt );

	float inline getDetA() const { return detA; }

	void setVertexWeight( int index, float weight );
	float getVertexWeight( int index );
	void onWeightChanged();

	inline int getNumberOfVertices() 
	{
		return numberOfVertices;
	}

	bool getRecalculateWeights() const { return recalculateWeights; }
	void setRecalculateWeights(bool recalculateWeights) { this->recalculateWeights = recalculateWeights; }

	void split( int v1Id, int v2Id );

	inline void getCenterOfMass( float* x, float* y, float* z )
	{
		*x = cm[0];
		*y = cm[1];
		*z = cm[2];
	}

protected:

	//static const float RESTITUTION_DISSIPATION;
	static const float C_YIELD;
	static const float C_CREEP;
	static const float C_MAX;
	static const float MIN_A_DET;
	static const float MAX_A_DET;

	DeformableMesh& mesh;

	float getMeshAlpha();
	float getMeshBeta();

	// outer product between two vectors
	void outerProduct( const float* v1, const float* v2, float* mat );

	//LAMatrix outerProduct( const float* v1, int size1, const float* v2, int size2 );
	void outerProduct( const float* v1, int size1, const float* v2, int size2, LAMatrix& mat, float weight );

	bool calculateRMatrix();

	void updateSpeedAndPosition( const float* relativeGi, int vertexIndex, float dt );
	
	LAMatrix Sp;

	void calculateAMatrix();
	void calculateRotationAngles( VMatrix& matR, float* rotX, float* rotY, float* rotZ );
	float getTotalMass();
	void resetWeights();
	void updateDeepness( float yPosition );
	void updateWhenFailToCalculateR();
	//void calculatingGoalPositions( float dt );
	// Id of the cluster
	int clusterId;

	// total number of vertices of the model
	int numberOfVertices;

	// current vertex buffer
	std::vector<ModelOBJ::Vertex*> m_vertexBuffer;

	// vertex speed
	std::vector<ModelOBJ::Vertex*> vertexSpeed;

	// intermediate coordinates
	std::vector<ModelOBJ::Vertex*> intermediatePosition;
	std::vector<ModelOBJ::Vertex*> intermediateSpeed;

	bool recalculateWeights;
	float deepness;

	// original vertex buffer
	std::vector<float> originalVertexBuffer;

	// original and updated center of mass and
	float cm0[3];
	float cm[3];

	// pi and qi vectors
	std::vector<float> p;
	std::vector<float> q;

	// vertex individual weight and total mass
	std::vector<float> originalWeight;
	std::vector<float> weight;

	// Apq matrix raw data
	float rawApq[16];

	VMatrix matR;
	VMatrix matA;
	VMatrix Aqq;
	float detA;
};

#endif
